
#ifndef VRP_BRDF_INCLUDED
#define VRP_BRDF_INCLUDED
#include "Common.hlsl"
#include "../Shaders/VLitInput.hlsl"
#include "VCommonMaterial.hlsl"
#define VkDielectricSpec half4(0.04, 0.04, 0.04, 1.0 - 0.04)
#define VFRESNEL0         half3(0.4,0.4,0.4)


struct VBRDFData
{
    half3 albedo;
    half3 diffuse;
    half3 specular;
    half3 fresnel0;
    half  reflectivity;
    half perceptualRoughness;
    half roughness;
    half roughness2;

    // Clear Coat Data
    float clearCoat;
    float clearCoatPerceptualRoughness;
    float clearCoatRoughness;

    half grazingTerm;
    half normalizationTerm;     // roughness * 4.0 + 2.0
    half roughness2MinusOne;    // roughness^2 - 1.0
};
struct VBRDF
{
    float3 diffR; // Diffuse  reflection   (T -> MS -> T, same sides)
    float3 specR; // Specular reflection   (R, RR, TRT, etc)
};
//计算漫反射率（metallic范围0-1，计算结果为0.96-0）
half VOneMinusReflectivityMetallic(half metallic)
{
    half oneMinusDielectricSpec = VkDielectricSpec.a;
    return oneMinusDielectricSpec - metallic * oneMinusDielectricSpec;
}
// Suppose clear coat's IOR is 1.5
float3 F0ClearCoatToSurface(float3 f0) {
    return saturate(f0 * (f0 * (.941892f - .263008f * f0) + .346479f) - .0285998f);
}
inline void VInitializeBRDFData(VInputData inputdata,
    half3 albedo, half metallic, 
    half smoothness, out VBRDFData outBRDFData);


inline void VInitializeClearCoatData(in VSurface surfaceData,in VInputData inputdata,
half3 albedo, half metallic, 
half smoothness, 
float clearCoat,float clearCoatRoughness ,float clearCoatPerceptualRoughness,
    out VBRDFData outBRDFData
    )
{
    half oneMinusReflectivity = VOneMinusReflectivityMetallic(metallic);//计算漫反射率，范围0.96-0
    half reflectivity = half(1.0) - oneMinusReflectivity;//计算镜面反射率，范围0.04-1
    half3 brdfDiffuse = albedo * oneMinusReflectivity;//计算漫反射颜色，oneMinusReflectivity为漫反射率
    //计算镜面反射颜色，范围0.04-albedo
    half3 brdfSpecular = lerp(VkDielectricSpec.rgb, albedo, metallic);
    
    outBRDFData.albedo = albedo;
    outBRDFData.diffuse = brdfDiffuse;
    outBRDFData.specular = brdfSpecular;
    
    outBRDFData.fresnel0=brdfSpecular;
    outBRDFData.reflectivity = reflectivity;
    outBRDFData.perceptualRoughness = VPerceptualSmoothnessToPerceptualRoughness(smoothness);
    outBRDFData.roughness           = max(VPerceptualRoughnessToRoughness(outBRDFData.perceptualRoughness), HALF_MIN_SQRT);
    outBRDFData.roughness2          = max(outBRDFData.roughness * outBRDFData.roughness, HALF_MIN);
    outBRDFData.grazingTerm         = saturate(smoothness + reflectivity);
    outBRDFData.normalizationTerm   = outBRDFData.roughness * half(4.0) + half(2.0);
    outBRDFData.roughness2MinusOne  = outBRDFData.roughness2 - half(1.0);

    
    
    #ifdef _CLEARCOAT
    outBRDFData.clearCoat=clamp(surfaceData.clearCoat, 1e-2, 1.0);
    outBRDFData.clearCoatRoughness=surfaceData.clearCoatRoughness;
    outBRDFData.clearCoatPerceptualRoughness=surfaceData.clearCoatPerceptualRoughness;
    
    // recompute f0 based on a clear coat-material interface instead of air-material, IOR has changed
    float3 f0Base = F0ClearCoatToSurface(outBRDFData.fresnel0);
    outBRDFData.fresnel0 = lerp(outBRDFData.fresnel0, f0Base, outBRDFData.clearCoat);
    outBRDFData.roughness = lerp(outBRDFData.roughness, max(outBRDFData.roughness, outBRDFData.clearCoatRoughness), outBRDFData.clearCoat);
    #endif
}
inline void VInitializeBRDFData(inout VSurface surfaceData, in VInputData input_data,out VBRDFData brdfData)
{
    
    #ifdef _CLEARCOAT
    VInitializeClearCoatData(surfaceData,input_data,
        surfaceData.albedo,surfaceData.metallic,surfaceData.smoothness,surfaceData.clearCoat,surfaceData.clearCoatRoughness,
        surfaceData.clearCoatPerceptualRoughness,brdfData
        );
    #else
    VInitializeBRDFData(input_data,surfaceData.albedo,surfaceData.metallic,surfaceData.smoothness,brdfData);
    #endif
}


float3 fresnelSchlickRoughness(float cosTheta, float3 F0, float roughness)
{
    return F0 + (max(float3(1.0 - roughness,1.0 - roughness,1.0 - roughness), F0) - F0) * pow(1.0 - cosTheta, 5.0);
} 
//使用Metallic（金属工作流）
/*
 *BaseMap：表示物体颜色，最终用来计算物体表面的漫反射颜色和镜面反射颜色。
 *MetallicMap：表示金属度，范围0-1，最终用来计算物体表面的漫反射率和镜面反射率。
 *Smoothness：表示光滑度，范围0-1，最终用来计算物体表面的粗糙度
 */
inline void VInitializeBRDFData(VInputData inputdata,
    half3 albedo, half metallic,
    half smoothness, out VBRDFData outBRDFData)
{
    half oneMinusReflectivity = VOneMinusReflectivityMetallic(metallic);//计算漫反射率，范围0.96-0
    half reflectivity = half(1.0) - oneMinusReflectivity;//计算镜面反射率，范围0.04-1
    half3 brdfDiffuse = albedo * oneMinusReflectivity;//计算漫反射颜色，oneMinusReflectivity为漫反射率
    //计算镜面反射颜色，范围0.04-albedo
    half3 brdfSpecular = lerp(VkDielectricSpec.rgb, albedo, metallic);
    //need to 
    outBRDFData.albedo = albedo;
    outBRDFData.diffuse = brdfDiffuse;
    outBRDFData.specular = brdfSpecular;
    
    outBRDFData.fresnel0=brdfSpecular;
    outBRDFData.reflectivity = reflectivity;
    outBRDFData.perceptualRoughness = VPerceptualSmoothnessToPerceptualRoughness(smoothness);
    outBRDFData.roughness           = max(VPerceptualRoughnessToRoughness(outBRDFData.perceptualRoughness), HALF_MIN_SQRT);
    outBRDFData.roughness2          = max(outBRDFData.roughness * outBRDFData.roughness, HALF_MIN);
    outBRDFData.grazingTerm         = saturate(smoothness + reflectivity);
    outBRDFData.normalizationTerm   = outBRDFData.roughness * half(4.0) + half(2.0);
    outBRDFData.roughness2MinusOne  = outBRDFData.roughness2 - half(1.0);
  

    outBRDFData.clearCoat=0;
    outBRDFData.clearCoatRoughness=0;
    outBRDFData.clearCoatPerceptualRoughness=0;
    
}






// D_ggx
real VD_GGX(real NdotH, real roughness)
{
    // float a = NdotH * roughness;
    // float k = roughness / (1.0 - NdotH * NdotH + a * a);
    // return k * k * (1.0 / PI);
    
    float a2            = roughness*roughness;
    float NH2           = NdotH*NdotH;
    float nominator     = a2;
    float denominator   = (NH2 * (a2-1.0) +1.0);
    denominator         = PI * denominator*denominator;
                
    return              nominator/ max(denominator,0.0000001) ;//防止分母为0
}
//F
float3 F_Schlick(float VoH, float3 f0) {
    float f = pow(1.0 - VoH, 5.0);
    return f + f0 * (1.0 - f);
}

//G
float GeometrySchlickGGX(float NoV,float roughness)
{
    float r = roughness +1.0;
    float k = r*r / 8.0;
    float nominator = NoV;
    float denominator = k + (1.0-k) * NoV;
    
    return              nominator/ max(denominator,0.0000001) ;//防止分母为0
}

float G_GeometrySmith(float3 NoV,float3 NoL,float roughness)
{
   

    float ggx1 = GeometrySchlickGGX(NoV,roughness);
    float ggx2 = GeometrySchlickGGX(NoL,roughness);

    return ggx1*ggx2;

}
float Fd_DisneyDiffuse(float NoV, float NoL, float LoH, float roughness) {
    float energyBias = lerp(0, 0.5, roughness);
    float energyFactor = lerp(1.0, 1.0 / 1.51, roughness);
    float f90 = energyBias + 2.0 * roughness * LoH * LoH;
    float lightScatter = F_Schlick(1.0, f90, NoL);
    float viewScatter = F_Schlick(1.0, f90, NoV);
    
    return lightScatter * viewScatter * energyFactor;

  
}
float V_SmithGGXCorrelated(float NoV, float NoL, float roughness) {
    float a2 = roughness * roughness;
    float GGXV = NoL * sqrt(NoV * NoV * (1.0 - a2) + a2);
    float GGXL = NoV * sqrt(NoL * NoL * (1.0 - a2) + a2);
    return 0.5 / (GGXV + GGXL);

}
float3 Specular_GGX(float NoH,float NoV,float NoL,float VoH,float Roughness,float3 F0)
{
    float D = VD_GGX(NoH,Roughness);
    float3 F = F_Schlick(VoH,F0);
    float G = G_GeometrySmith(NoV,NoL,Roughness);
    float3 nominator = D*F*G;
    float denominator = max(4*NoV*NoL,0.001);
    float3 Specular = nominator/denominator;
    Specular =max( Specular,0);
    return Specular;
}
//https://blog.selfshadow.com/publications/s2017-shading-course/imageworks/s2017_pbs_imageworks_slides_v2.pdf
float3 AverageFresnel(float3 r, float3 g)
{
    return float3(0.087237,0.087237,0.087237) + 0.0230685*g - 0.0864902*g*g + 0.0774594*g*g*g
           + 0.782654*r - 0.136432*r*r + 0.278708*r*r*r
           + 0.19744*g*r + 0.0360605*g*g*r - 0.2586*g*r*r;
}
float3 MultiScatterBRDF(in float NdotL,in float NdotV,in float3 albedo,in float uRoughness)
{
    

    float3 E_o = Get_Eu(NdotL,uRoughness);
    float3 E_i = Get_Eu(NdotV,uRoughness);
    

    float3 E_avg =Get_Eavg(uRoughness);
        
    // copper
    float3 edgetint = float3(0.827, 0.792, 0.678);
    float3 F_avg = AverageFresnel(albedo, edgetint);
  
    // TODO: To calculate fms and missing energy here
    float3 F_ms = (1.0 - E_o) * (1.0 - E_i) / (PI * (1.0 - E_avg));
    float3 F_add = F_avg * E_avg / (1.0 - F_avg * (1.0 - E_avg));

    return F_add * F_ms;
  
}

//VBRDFData brdfData, Light light, float3 viewDirectionWS, float3 normalWS
float3 standardBRDF(VBRDFData brdf, Light light, float3 viewDirectionWS, float3 normalWS)
{
    float l=normalize(light.direction);
    float3 h = normalize(viewDirectionWS + l);

    float NoV = abs(dot(normalWS, viewDirectionWS)) + 1e-5;
    float NoL = clamp(dot(normalWS, l), 0.0, 1.0);
    float NoH = clamp(dot(normalWS, h), 0.0, 1.0);
    float VoH = clamp(dot(viewDirectionWS, h), 0.0, 1.0);
    float LoH = clamp(dot(l, h), 0.0, 1.0);

    float roughness = brdf.roughness;

    
    float D = VD_GGX(NoH,brdf.roughness);
    float3 F = F_Schlick(VoH,brdf.fresnel0);
    float G = G_GeometrySmith(NoV,NoL,brdf.roughness);
    float3 nominator = D*F*G;
    float denominator = max(4*NoV*NoL,0.001);
    
    brdf.specular=F;
    
    // specular BRDF
    float3 Fr = nominator/denominator;
    #if defined(_MULTISCATTERING)
    float3 energyCompensation = MultiScatterBRDF(NoL,NoV,brdf.albedo,roughness);
    Fr+=energyCompensation;
    //return energyCompensation*NoL;
    #endif
    
    // diffuse BRDF
    float3 Fd = brdf.diffuse * Fd_DisneyDiffuse(NoV, NoL, LoH, brdf.roughness);
    #if defined(_NOSpecularactor)
    Fr=float3(0.0f,0,0);
    #endif
    return (Fd + Fr) * NoL;
}
float V_Kelemen(float LoH)
{
    return 0.25 / (LoH * LoH);
}
float3 clearCoatBRDF(VBRDFData brdf, Light light, float3 viewDirectionWS, float3 normalWS)
{
    float l=normalize(light.direction);
    float3 h = normalize(viewDirectionWS + l);

    float NoV = abs(dot(normalWS, viewDirectionWS)) + 1e-5;
    float NoL = clamp(dot(normalWS, l), 0.0, 1.0);
    float NoH = clamp(dot(normalWS, h), 0.0, 1.0);
    float VoH = clamp(dot(viewDirectionWS, h), 0.0, 1.0);
    float LoH = clamp(dot(l, h), 0.0, 1.0);

    float roughness = brdf.roughness;

    
    float D = VD_GGX(NoH,brdf.roughness);
    float3 F = F_Schlick(VoH,brdf.fresnel0);
    float G = G_GeometrySmith(NoV,NoL,brdf.roughness);
    float3 nominator = D*F*G;
    float denominator = max(4*NoV*NoL,0.001);
    
    brdf.specular=F;
    
    // specular BRDF
    float3 Fr = nominator/denominator;
    
    #if defined(_MULTISCATTERING)
    float3 energyCompensation = MultiScatterBRDF(NoL,NoV,brdf.albedo,roughness);
    Fr+=energyCompensation;
    #endif
    // diffuse BRDF
    float3 Fd = brdf.diffuse * Fd_DisneyDiffuse(NoV, NoL, LoH, brdf.roughness);

    // clear coat BRDF
    float Dc = VD_GGX(NoH,brdf.roughness);
    float3 Fc = F_Schlick(VoH,brdf.fresnel0);
    float GC = G_GeometrySmith(NoV,NoL,brdf.roughness);
    nominator = Dc*Fc*GC;
    denominator = max(4*NoV*NoL,0.001);
    
    float3 Frc = nominator/ denominator;
    #if defined(_NOSpecularactor)
    Fr=float3(0.0f,0,0);
    #endif
    float3 cloat=(Fd + Fr)*Fc+Frc ;
    
    return (cloat)*NoL;
   
}

















float3 DirectionBRDFDiffTerm(VBRDFData brdfData, float NdotL, float clampNdotV, float LdotV)
{

    float diffTerm = DisneyDiffuse(clampNdotV, abs(NdotL), LdotV, brdfData.perceptualRoughness);
    return diffTerm.xxx;
}
float3 DirectionBRDFSpecTerm(VBRDFData brdfData, float NdotL, float clampNdotV, float NdotH, float LdotH)
{
    float partLambdaV = GetSmithJointGGXPartLambdaV(clampNdotV, brdfData.roughness);
    float3 F = F_Schlick(brdfData.specular, LdotH);
    float DV = DV_SmithJointGGX(NdotH, abs(NdotL), clampNdotV, brdfData.roughness, partLambdaV);
    return DV * F;
}



VBRDF VEvaluateBRDF(VBRDFData brdfData, Light light, float3 viewDirectionWS, float3 normalWS)
{
    VBRDF cbrdf;

    float3 H = normalize(viewDirectionWS + light.direction);
    float NdotL = dot(normalWS, light.direction);
    float NdotV = dot(normalWS, viewDirectionWS);
    float clampNdotL = max(saturate(NdotL), 0.00001);
    float clampNdotV = ClampNdotV(NdotV);

    float NdotH = saturate(dot(normalWS, H));
    float LdotH = dot(light.direction, H);
    float LdotV = dot(light.direction, viewDirectionWS);
//kd ks
    float3  F = F_Schlick(NdotH, brdfData.fresnel0);
    brdfData.specular=F;
    brdfData.diffuse*=(1-F);
    cbrdf.diffR = DirectionBRDFDiffTerm(brdfData, NdotL, clampNdotV, LdotV) * clampNdotL;
    cbrdf.specR = DirectionBRDFSpecTerm(brdfData, NdotL, clampNdotV, NdotH, LdotH) * clampNdotL;
    return cbrdf;
}

#endif